﻿using DevExpress.ExpressApp.Layout;
using DevExpress.ExpressApp.Model;
using DevExpress.ExpressApp.Model.Core;
using DevExpress.ExpressApp.Model.NodeGenerators;
using DevExpress.Persistent.Base;
using System.Drawing;
using System.Reflection;
using System.Xml.Linq;

namespace EasyXaf.ViewLayoutBuilder;

class DetailViewLayoutNodesGeneratorUpdater : ModelNodesGeneratorUpdater<ModelDetailViewLayoutNodesGenerator>
{
    private IModelDetailView _modelDetailView;

    private static XElement GetLayout(IModelDetailView modelDetailView)
    {
        var typeInfo = modelDetailView.ModelClass?.TypeInfo;
        var attribute = typeInfo?.FindAttribute<DetailViewLayoutBuilderAttribute>();
        if (attribute != null)
        {
            var buildLayoutMethod = typeInfo.Type.GetMethod("BuildLayout", BindingFlags.Static | BindingFlags.Public);
            if (buildLayoutMethod != null)
            {
                var layout = buildLayoutMethod.Invoke(null, Array.Empty<object>()) + string.Empty;
                if (!string.IsNullOrWhiteSpace(layout))
                {
                    return XElement.Parse(layout);
                }
            }
        }
        return null;
    }

    private static string GetLayoutElementId(IModelNode parentNode, string category)
    {
        var nodes = new List<IModelNode>();
        for (var i = 0; i < parentNode.NodeCount; i++)
        {
            nodes.Add(parentNode.GetNode(i));
        }

        var maxNumber = nodes
            .OfType<IModelViewLayoutElement>()
            .Where(node => node.Id?.StartsWith(category) == true)
            .Select(node =>
            {
                if (int.TryParse(node.Id[category.Length..], out int number))
                {
                    return number;
                }
                return 0;
            })
            .OrderBy(number => number)
            .LastOrDefault();

        return $"{category}{maxNumber + 1:00}";
    }

    private static void InitializeLayoutElement(IModelViewLayoutElement modelViewLayoutElement, XElement element)
    {
        if (modelViewLayoutElement is IModelLayoutElementWithCaptionOptions modelLayoutElementWithCaptionOptions)
        {
            if (bool.TryParse(element.Attribute("show-caption")?.Value, out bool showCaption))
            {
                modelLayoutElementWithCaptionOptions.ShowCaption = showCaption;
            }

            var captionLocation = element.Attribute("caption-location")?.Value;
            if (!string.IsNullOrWhiteSpace(captionLocation))
            {
                Locations location = Locations.Default;

                switch (captionLocation)
                {
                    case "left":
                        location = Locations.Left;
                        break;
                    case "right":
                        location = Locations.Right;
                        break;
                    case "top":
                        location = Locations.Top;
                        break;
                    case "bottom":
                        location = Locations.Bottom;
                        break;
                }

                modelLayoutElementWithCaptionOptions.CaptionLocation = location;
            }
        }

        if (modelViewLayoutElement is IModelLayoutElementWithCaption modelLayoutElementWithCaption)
        {
            var caption = element.Attribute("caption")?.Value;
            if (!string.IsNullOrEmpty(caption))
            {
                modelLayoutElementWithCaption.Caption = caption;
            }
        }

        if (modelViewLayoutElement is IModelLayoutItem modelLayoutItem)
        {
            if (element.Attribute("size-constraints-type")?.Value == "custom")
            {
                modelLayoutItem.SizeConstraintsType = XafSizeConstraintsType.Custom;
            }

            var minSize = element.Attribute("min-size")?.Value;
            if (!string.IsNullOrWhiteSpace(minSize))
            {
                var value = new SizeConverter().ConvertFrom(minSize);
                if (value != null)
                {
                    modelLayoutItem.MinSize = (Size)value;
                }
            }

            var maxSize = element.Attribute("max-size")?.Value;
            if (!string.IsNullOrWhiteSpace(maxSize))
            {
                var value = new SizeConverter().ConvertFrom(maxSize);
                if (value != null)
                {
                    modelLayoutItem.MaxSize = (Size)value;
                }
            }
        }

        if (double.TryParse(element.Attribute("size")?.Value, out double size))
        {
            modelViewLayoutElement.RelativeSize = size;
        }
        else if (double.TryParse(element.Attribute("relative-size")?.Value, out double relativeSize))
        {
            modelViewLayoutElement.RelativeSize = relativeSize;
        }
    }

    private IModelLayoutViewItem CreateLayoutViewItem(IModelNode parentNode, XElement element)
    {
        var viewItem = _modelDetailView.Items.FirstOrDefault(item => item.Id == element.Value);
        if (viewItem != null)
        {
            var layoutViewItem = parentNode.AddNode<IModelLayoutViewItem>();
            layoutViewItem.Id = viewItem.Id;
            layoutViewItem.ViewItem = viewItem;
            return layoutViewItem;
        }
        return null;
    }

    private static IModelLayoutGroup CreateLayoutGroup(IModelNode parentNode, XElement element)
    {
        IModelLayoutGroup group = null;

        var name = element.Name;
        var parentName = element.Parent?.Name;

        if (name == "h")
        {
            group = parentNode.AddNode<IModelLayoutGroup>();
            group.Direction = FlowDirection.Horizontal;
        }
        else if (name == "v")
        {
            group = parentNode.AddNode<IModelLayoutGroup>();
            group.Direction = FlowDirection.Vertical;
        }

        if (group != null)
        {
            var imageName = element.Attribute("image")?.Value;
            if (string.IsNullOrWhiteSpace(imageName))
            {
                imageName = element.Attribute("image-name")?.Value;
            }

            if (!string.IsNullOrWhiteSpace(imageName))
            {
                group.ImageName = imageName;
            }

            if (parentName == "t")
            {
                group.Id = GetLayoutElementId(parentNode, "Tab");
            }
            else
            {
                group.Id = GetLayoutElementId(parentNode, "Group");
            }
        }

        return group;
    }

    private static IModelTabbedGroup CreateTabbedGroup(IModelNode parentNode, XElement element)
    {
        if (element.Name == "t")
        {
            var tabbedGroup = parentNode.AddNode<IModelTabbedGroup>();
            tabbedGroup.Id = GetLayoutElementId(parentNode, "TabGroup");
            return tabbedGroup;
        }
        return null;
    }

    private IModelViewLayoutElement CreateLayoutItem(IModelNode parentNode, XElement element, string id = null, int index = 0)
    {
        IModelViewLayoutElement modelViewLayoutElement = null;

        switch (element.Name.LocalName)
        {
            case "h":
            case "v":
                modelViewLayoutElement = CreateLayoutGroup(parentNode, element);
                break;
            case "t":
                modelViewLayoutElement = CreateTabbedGroup(parentNode, element);
                break;
            case "p":
                modelViewLayoutElement = CreateLayoutViewItem(parentNode, element);
                break;
        }

        if (modelViewLayoutElement != null)
        {
            var childIndex = 0;
            foreach (var child in element.Elements())
            {
                CreateLayoutItem(modelViewLayoutElement, child, index: childIndex);
                childIndex++;
            }

            InitializeLayoutElement(modelViewLayoutElement, element);

            id ??= element.Attribute("id")?.Value;
            if (!string.IsNullOrWhiteSpace(id))
            {
                modelViewLayoutElement.Id = id;
            }

            modelViewLayoutElement.Index = index;
        }

        return modelViewLayoutElement;
    }

    public override void UpdateNode(ModelNode modelNode)
    {
        if (modelNode is IModelViewLayout modelViewLayout)
        {
            if (modelViewLayout.Parent is IModelDetailView modelDetailView)
            {
                var layout = GetLayout(modelDetailView);
                if (layout == null)
                {
                    return;
                }

                modelViewLayout.ClearNodes();
                _modelDetailView = modelDetailView;

                CreateLayoutItem(modelViewLayout, layout, ModelDetailViewLayoutNodesGenerator.MainLayoutGroupName);
            }
        }
    }
}
